package pt.iscte.pcd.project.local;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.LinkedList;

import pt.iscte.pcd.project.exceptions.FileSystemException;
import pt.iscte.pcd.project.interfaces.EntryType;
import pt.iscte.pcd.project.interfaces.PCDDirectory;
import pt.iscte.pcd.project.interfaces.PCDFile;
import pt.iscte.pcd.project.server.ServerWorker;

public class LocalFile extends LocalEntry implements PCDFile {
	private int nrPessoasALer ;
	private int nrPessoasAEscrever;
	private LinkedList<ServerWorker> pessoasAEscrever = new LinkedList<ServerWorker>();
	
	
	public LocalFile(String nome, PCDDirectory parent) {
		super(nome, EntryType.FILE, parent);
	}

	/**
	 * return the size of the file
	 * @return int
	 * @throws FileSystemException
	 */
	@Override
	public int length() throws FileSystemException {
		java.io.File file = new java.io.File(getFullPath());
		return (int) file.length();
	}

	/**
	 * return an array of byte 
	 * @return byte []
	 * @throws FileSystemException
	 */
	@Override
	public byte[] read() throws FileSystemException {
		return read(0, length());
	}

	/**
	 * write to a file
	 */
	@Override
	public void write(byte[] dataToWrite) throws FileSystemException {
		write(dataToWrite, 0);
	}

	/**
	 * read from a file
	 * @param offset
	 * @param length
	 * @return
	 * @throws FileSystemException
	 */
	public byte[] read(int offset, int length) throws FileSystemException {
		ByteBuffer buffer = null;
		RandomAccessFile aFile = null;
		try {
			aFile = new RandomAccessFile(getFullPath(), "r");
			FileChannel channel = aFile.getChannel();

			if (offset > aFile.length())
				return null;

			if (offset + length > aFile.length()) {
				length = (int) aFile.length() - offset;
			}

			channel.position(offset);
			buffer = ByteBuffer.allocate(length);
			channel.read(buffer);

		} catch (IOException e) {
			throw new FileSystemException(0, e.getMessage());
		} finally {
			if (aFile != null)
				try {
					aFile.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		return buffer.array();
	}

	/**
	 * write to a file
	 * @param dataToWrite
	 * @param offset
	 * @throws FileSystemException
	 */
	public void write(byte[] dataToWrite, int offset)
			throws FileSystemException {
		RandomAccessFile aFile = null;
		try {
			aFile = new RandomAccessFile(getFullPath(), "rw");
			FileChannel channel = aFile.getChannel();
			if (offset > aFile.length())
				offset = (int) aFile.length();

			ByteBuffer buf = ByteBuffer.wrap(dataToWrite);
			channel.position(offset);

			while (buf.hasRemaining()) {
				channel.write(buf);
			}
		} catch (IOException e) {
			throw new FileSystemException(0, e.getMessage());
		} finally {
			if (aFile != null)
				try {
					aFile.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	/**
	 * create an new file
	 */
	@Override
	public void createNew() {
		try {
			super.getFile().createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Synchronized method to see if where are less than 10 people reading
	 * @return boolean 
	 */
	@Override
	public synchronized boolean readLock() {
		return getNrPessoasALer() < 10;
		
	}
	
	/**
	 * Synchronized method to see if where someone reading
	 * @return boolean 
	 */
	@Override
	public synchronized boolean writeLock() {
		return getNrPessoasAEscrever() == 0;
	}
	/**
	 * Synchronized get to see the number of people reading 
	 * @return
	 */
	public synchronized int getNrPessoasALer() {
		return nrPessoasALer;
	}

	/**
	 * Synchronized Set for the people reading
	 * @param int nrPessoasALer
	 */
	public synchronized void setNrPessoasALer(int nrPessoasALer) {
		this.nrPessoasALer = nrPessoasALer;
	}
	
	/**
	 * change the number of people reading
	 */
	@Override
	public void readUnlock() {
		setNrPessoasALer(getNrPessoasALer()-1);
	}

	/**
	 * change the number of people writing
	 */
	@Override
	public void writeUnlock() {
		setNrPessoasAEscrever(getNrPessoasAEscrever()-1);
		
	}
	

	/**
	 * Synchronized get for people writing
	 * @return int nrPessoasAEscrever
	 */
	public synchronized int getNrPessoasAEscrever() {
		return nrPessoasAEscrever;
	}

	/**
	 * Synchronized set for the number of people writing
	 * @param int nrPessoasAEscrever
	 */
	public synchronized void setNrPessoasAEscrever(int nrPessoasAEscrever) {
		this.nrPessoasAEscrever = nrPessoasAEscrever;
	}

	public LinkedList<ServerWorker> getPessoasAEscrever() {
		return pessoasAEscrever;
	}

	public void setPessoasAEscrever(LinkedList<ServerWorker> pessoasAEscrever) {
		this.pessoasAEscrever = pessoasAEscrever;
	}

}
